use async_process::Command;
use std::{
    collections::HashMap,
    fmt::Display,
    io::BufRead,
    process::{ExitStatus, Stdio},
};

mod ap_info;
mod err;
mod iw_res;

pub use ap_info::{BandWidth, SecondChannel, Security, Standard, BSS};
pub use err::IWError;
use err::Result;
use iw_res::*;

pub struct IW {
    can_sudo: bool,
}

#[derive(Debug, Clone, Copy)]
pub enum Mode {
    Managed,
    Ibss,
    Monitor,
    Mesh,
    Wds,
}
impl Mode {
    fn name(&self) -> &'static str {
        match self {
            Mode::Managed => "managed",
            Mode::Ibss => "ibss",
            Mode::Monitor => "monitor",
            Mode::Mesh => "mesh",
            Mode::Wds => "wds",
        }
    }
}

#[derive(Clone)]
pub struct Interface {
    ifname: String,
}
impl Display for Interface {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.ifname)
    }
}
impl From<String> for Interface {
    fn from(ifname: String) -> Self {
        Self { ifname }
    }
}
impl From<&str> for Interface {
    fn from(value: &str) -> Self {
        Self {
            ifname: value.to_string(),
        }
    }
}
impl AsRef<str> for Interface {
    fn as_ref(&self) -> &str {
        self.ifname.as_str()
    }
}

impl IW {
    pub async fn new() -> Result<Self> {
        // let can_sudo = can_sudo().await;
        let can_sudo = false;
        check_command("iw").await?;
        check_command("ifconfig").await?;
        Ok(IW { can_sudo })
    }

    pub async fn interface_list(&self) -> Result<Vec<Interface>> {
        let output = Command::new("sh")
            .arg("-c")
            .arg("iw dev | grep Interface")
            .output()
            .await
            .map_err(|e| IWError::Unknown(format!("Failed to execute command: {}", e)))?;

        let lines = output.stdout.lines();

        // 过滤和解析出网卡名称，这里假设网卡名称位于 "Interface <name>" 后面
        let mut interfaces = Vec::new();
        for line in lines.map_while(|i| i.ok()) {
            if let Some(interface_name) = line.split_whitespace().nth(1) {
                interfaces.push(interface_name.into());
            }
        }
        Ok(interfaces)
    }

    fn command(&self, program: &str) -> Command {
        if self.can_sudo {
            let mut cmd = Command::new("sudo");
            cmd.stdout(Stdio::inherit());
            cmd.stdin(Stdio::inherit());
            cmd.arg(program);
            cmd
        } else {
            Command::new(program)
        }
    }

    async fn info(&self, interface: &Interface) -> anyhow::Result<Info> {
        let output = self
            .command("iw")
            .args(["dev", interface.as_ref(), "info"])
            .output()
            .await?;

        let result = String::from_utf8_lossy(&output.stdout);

        Info::parse(&result)
    }

    pub async fn freq_max_bandwidth(
        &self,
        interface: &Interface,
    ) -> anyhow::Result<HashMap<i32, BandWidth>> {
        let mut out = HashMap::new();
        let info = self.info(interface).await?;
        let output = self
            .command("iw")
            .arg("phy")
            .arg(format!("phy{}", info.wiphy))
            .arg("info")
            .output()
            .await?;

        let result = String::from_utf8_lossy(&output.stdout);

        let info = PhyInfo::parse(&result)?;

        for band in info.bands {
            let mut max_width = BandWidth::HT20;
            if band.capabilities.rx_ht40_sgi {
                max_width = BandWidth::HT40;
            }
            if band.vht_capabilities.mhz80 {
                max_width = BandWidth::MHz80;
            }
            if band.vht_capabilities.mhz160 {
                max_width = BandWidth::MHz160;
            }

            for freq in band.frequencies {
                out.insert(freq, max_width);
            }
        }

        Ok(out)
    }

    pub async fn ifdown(&self, interface: &Interface) -> Result<()> {
        let status = self
            .command("ifconfig")
            .arg(interface.as_ref())
            .arg("down")
            .status()
            .await?;

        status.check(format!(
            "Failed to bring down interface {}",
            interface.as_ref()
        ))
    }
    pub async fn ifup(&self, interface: &Interface) -> Result<()> {
        let status = self
            .command("ifconfig")
            .arg(interface.as_ref())
            .arg("up")
            .status()
            .await?;

        status.check(format!(
            "Failed to bring up interface {}",
            interface.as_ref()
        ))
    }
    pub async fn set_mode(&self, interface: &Interface, mode: Mode) -> Result<()> {
        self.ifdown(interface).await?;
        let status = self
            .command("iw")
            .arg("dev")
            .arg(interface.as_ref())
            .arg("set")
            .arg("type")
            .arg(mode.name())
            .status()
            .await?;

        status.check(format!(
            "Failed to set mode for interface {}",
            interface.as_ref()
        ))?;

        self.ifup(interface).await?;
        Ok(())
    }

    pub async fn scan(&self, interface: &Interface) -> Result<Vec<BSS>> {
        self.set_mode(interface, Mode::Managed).await?;

        let output = self
            .command("iw")
            .args(["dev", interface.as_ref(), "scan"])
            .output()
            .await?;

        output.status.check(format!(
            "scan fail: {}",
            String::from_utf8_lossy(&output.stderr)
        ))?;

        let result = String::from_utf8_lossy(&output.stdout);

        let out = BSS::parse_all(result.as_ref()).unwrap();

        Ok(out)
    }

    pub async fn set_channel(
        &self,
        interface: &Interface,
        channel: i32,
        band_width: Option<BandWidth>,
        second: Option<SecondChannel>,
    ) -> Result<()> {
        let mut cmd = self.command("iw");
        let c = cmd
            .arg("dev")
            .arg(interface.as_ref())
            .arg("set")
            .arg("channel")
            .arg(format!("{}", channel));

        if let Some(b) = band_width {
            match b {
                BandWidth::HT40 => {
                    if let Some(second) = second {
                        c.arg(match second {
                            SecondChannel::Below => "HT40-",
                            SecondChannel::Above => "HT40+",
                        });
                    }
                }
                _ => {
                    c.arg(b.cmd());
                }
            }
        }

        let status = c.status().await?;

        status.check(format!(
            "Failed to set channel for interface {}",
            interface.as_ref()
        ))
    }

    pub async fn set_channel_by_bss(&self, interface: &Interface, bss: &BSS) -> Result<()> {
        self.set_channel(interface, bss.channel, Some(bss.width), bss.second)
            .await
    }
    pub async fn set_freq_by_bss(&self, interface: &Interface, bss: &BSS) -> Result<()> {
        self.set_freq(interface, bss.freq_mhz, Some(bss.width), bss.second)
            .await
    }
    pub async fn set_freq(
        &self,
        interface: &Interface,
        freq_mhz: i32,
        band_width: Option<BandWidth>,
        second: Option<SecondChannel>,
    ) -> Result<()> {
        let mut cmd = self.command("iw");
        let c = cmd
            .arg("dev")
            .arg(interface.as_ref())
            .arg("set")
            .arg("freq")
            .arg(format!("{}", freq_mhz));

        if let Some(b) = band_width {
            match b {
                BandWidth::HT40 => {
                    if let Some(second) = second {
                        c.arg(match second {
                            SecondChannel::Below => "HT40-",
                            SecondChannel::Above => "HT40+",
                        });
                    }
                }
                _ => {
                    c.arg(b.cmd());
                }
            }
        }

        let status = c.status().await?;

        status.check(format!(
            "Failed to set channel for interface {}",
            interface.as_ref()
        ))
    }
}

// async fn can_sudo() -> bool {
//     let output = Command::new("sudo").arg("whoami").output().await;

//     match output {
//         Ok(o) => o.status.success(),
//         Err(_) => false,
//     }
// }

async fn check_command(cmd: &str) -> Result<()> {
    Command::new(cmd)
        .arg("--help")
        .output()
        .await
        .map_err(|e| IWError::NotSupport(format!("command [{}] fail: {:?}", cmd, e)))?;
    Ok(())
}

trait ExitStatusExt {
    fn check(&self, fail_msg: String) -> Result<()>;
}

impl ExitStatusExt for ExitStatus {
    fn check(&self, fail_msg: String) -> Result<()> {
        if !self.success() {
            return Err(IWError::Unknown(fail_msg));
        }
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[tokio::test]
    async fn test_iw_ifconfig() {
        check_command("iw").await.unwrap();
        check_command("ifconfig").await.unwrap();
    }

    #[tokio::test]
    async fn test_check() {
        assert!(check_command("asfsadasd").await.is_err());
    }

    #[tokio::test]
    async fn test_info() {
        let iw = IW::new().await.unwrap();

        let list = iw.interface_list().await.expect("无可用网卡");
        for card in &list {
            println!("ifname: {}", card);
            let info = iw.info(card).await.unwrap();
            println!("{:?}", info)
        }
    }

    #[tokio::test]
    async fn test_max_band() {
        let iw = IW::new().await.unwrap();

        let list = iw.interface_list().await.expect("无可用网卡");
        for card in &list {
            println!("ifname: {}", card);
            let info = iw.freq_max_bandwidth(card).await.unwrap();
            println!("{:?}", info)
        }
    }
}
